#region copyright
// Halcyone - Silverlight Application Framework
// Copyright (C) 2009 - 2010 Alexey Zakharov (B&Z LLC)
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;

namespace Witcraft.Halcyone.ASPFacility
{
    /// <summary>
    /// The per request life time manager storage.
    /// </summary>
    public class PerRequestLifeTimeManagerStorage
    {
        #region Constants and Fields

        /// <summary>
        /// The per request lifetime manager storage key.
        /// </summary>
        public const string PerRequestLifetimeManagerStorageKey = "PerRequestLifeTimeManagerStorage";

        /// <summary>
        /// Backing store for relating keys to object instances for Unity.
        /// </summary>
        private readonly Dictionary<Guid, object> instances = new Dictionary<Guid, object>();

        #endregion

        #region Properties

        /// <summary>
        /// Gets Current.
        /// </summary>
        public static PerRequestLifeTimeManagerStorage Current
        {
            get
            {
                IDictionary httpContextItems = HttpContext.Current.Items;
                lock (httpContextItems)
                {
                    if (!httpContextItems.Contains(PerRequestLifetimeManagerStorageKey))
                    {
                        var perRequestLifeTimeManagerStorage = new PerRequestLifeTimeManagerStorage();
                        HttpContext.Current.Items[PerRequestLifetimeManagerStorageKey] =
                            perRequestLifeTimeManagerStorage;
                    }
                }

                return
                    HttpContext.Current.Items[PerRequestLifetimeManagerStorageKey] as PerRequestLifeTimeManagerStorage;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The clear storage.
        /// </summary>
        public void ClearStorage()
        {
            var keys = new List<Guid>(instances.Keys);

            for (int i = 0; i < keys.Count; i++)
            {
                RemoveInstance(keys[i]);
            }
        }

        /// <summary>
        /// Finds the object associated with the given key.
        /// </summary>
        /// <param name="key">
        /// Key used to find the associated object instance.
        /// </param>
        /// <returns>
        /// The object instance associated with the supplied key.  If no instance is registered, null is returned.
        /// </returns>
        public object FindInstance(Guid key)
        {
            object obj = null;

            // We don't care whether or not this succeeds or fails.
            instances.TryGetValue(key, out obj);

            return obj;
        }

        /// <summary>
        /// Registers the given instance with the given key into the backing store.
        /// </summary>
        /// <param name="key">
        /// Key to associate with the object instance.
        /// </param>
        /// <param name="value">
        /// Object instance to associate with the given key in the backing store.
        /// </param>
        public void RegisterInstance(Guid key, object value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            instances.Add(key, value);
        }

        /// <summary>
        /// Removes the given key from the backing store.  This method will also dispose of the associated object instance if it implements <see cref="System.IDisposable"/>.
        /// </summary>
        /// <param name="key">
        /// Key to remove from the backing store.
        /// </param>
        public void RemoveInstance(Guid key)
        {
            // We don't want to use FindInstance JUST IN CASE somehow a key gets in there with a null object.
            object instance = null;

            if (instances.ContainsKey(key))
            {
                // Get the instance.
                instance = instances[key];

                // See if it needs disposing.
                var disposable = instance as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }

                // Remove it from the instances list.
                instances.Remove(key);
            }
        }

        #endregion
    }
}